package com.hippo.drools.config;

import com.hippo.drools.utils.KieUtils;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
 * @ClassName RuleEngineConfig
 * @Description TODO drools规则引擎配置类
 * @Author tangxl
 * @create 2023-05-08 10:26
 **/
@Slf4j
@Configuration
public class RuleEngineConfig {
    /**
     * 规则文件路径
     */
    public static final String RULES_PATH = "rules/";
    public static final String BASE_RULES_PATH = "classpath*:";

    /**
     * 读取规则文件，并将规则文件写入到规则引擎系统内
     * @return
     * @throws IOException
     * @ConditionalOnMissingBean，它是修饰bean的一个注解， 主要实现的是，当你的bean被注册之后，如果而注册相同类型的bean，就不会成功，
     * 它会保证你的bean只有一个，即你的实例只有一个，当你注册多个相同的bean时，会出现异常
     */
    @Bean
    @ConditionalOnMissingBean(KieFileSystem.class)
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
        //获取初始化规则文件所在路径
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        Resource[] files = resourcePatternResolver.getResources(BASE_RULES_PATH + RULES_PATH + "**/*");
        String path = null;
        for (Resource file : files) {
            path = RULES_PATH + file.getFilename();
            URI uri = file.getURI();
            file.getURL();
            log.info("path=" + file.getFile().getAbsolutePath() + path);
            //将规则文件写规则引擎系统内
            org.kie.api.io.Resource resource = ResourceFactory.newClassPathResource(path, "UTF-8");
            kieFileSystem.write(resource);
        }
        return kieFileSystem;
    }

    /**
     * 获取KieServices,它是一个单例的KieServices实例，
     * 它是一个工厂类，它可以创建KieContainer、KieRepository、KieBase、KieSession等实例
     * @return
     */
    private KieServices getKieServices()
    {
        return KieServices.Factory.get();
    }

    /**
     * 获取KieContainer，KieContainer是一个KieBase的容器，KieBase是一个规则引擎知识库，它包含了若干的规则（Rule），
     * @return
     * @throws IOException
     */
    @Bean
    @ConditionalOnMissingBean(KieContainer.class)
    public KieContainer kieContainer() throws IOException {
        //
        final KieRepository kieRepository = getKieServices().getRepository();
        //设置时间格式
        System.setProperty("drools.dateformat","yyyy-MM-dd HH:mm");
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        //设置KieContainer,getDefaultReleaseId()获取默认的ReleaseId
        KieContainer kieContainer = getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
        // 设置KieUtils的KieContainer,方便后续获取热加载
        KieUtils.setKieContainer(kieContainer);
        return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
    }

    /**
     * 获取KieBase
     */
    @Bean
    @ConditionalOnMissingBean(KieBase.class)
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }

    /**
     * 获取KieSession
     * @return
     * @throws IOException
     */
    @Bean
    @ConditionalOnMissingBean(KieSession.class)
    public KieSession kieSession() throws IOException {
        KieSession kieSession = kieContainer().newKieSession();
        KieUtils.setKieSession(kieSession);
        return kieSession;
    }

    /**
     * 设置KieModuleBeanFactoryPostProcessor，它是一个BeanFactoryPostProcessor，它的作用是将KieModule注册到Spring容器中，
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(KModuleBeanFactoryPostProcessor.class)
    public KModuleBeanFactoryPostProcessor kModuleBeanFactoryPostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }

}
